package com.github.livebase.util.base;

import java.io.ByteArrayInputStream;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class ByteArray {
    private byte[] bs;
    private int length;
    private int capacity;
    private int expandSize;
    private int maxSize;
    public ByteArray(){
        this(1024);
    }
    public ByteArray(byte[] data){
        this(data, 1024);
    }
    public ByteArray(int expandSize){
        this(expandSize, Integer.MAX_VALUE);
    }
    public ByteArray(int expandSize, int maxSize){
        this(new byte[0], expandSize, maxSize);
    }
    public ByteArray(byte[] data, int expandSize) {
        this(data, expandSize, Integer.MAX_VALUE);
    }
    public ByteArray(byte[] data, int expandSize, int maxSize){
        maxSize = Math.max(Math.max(data.length, expandSize), maxSize);
        this.bs = data;
        this.length = data.length;
        this.capacity = data.length;
        this.expandSize = expandSize;
        this.maxSize = maxSize;
    }
    public void append(byte append){
        int remaining = this.length + 1 - this.capacity;
        if (0 < remaining) {
            bs = expand(bs, this.expandSize);
            this.capacity = this.capacity + this.expandSize;
        }
        bs[length++] = append;
    }
    public int at(int pos) {
        if (pos >= this.length) {
            return -1;
        }
        if (pos < 0) {
            if (pos < -this.length) {
                return -1;
            }
            return at(this.length + pos);
        }
        return this.bs[pos];
    }
    public void clear() {
        this.length = 0;
    }
    public void removeLast(int size) {
        this.length -= size;
        if (this.length < 0) {
            this.length = 0;
        }
    }
    public void append(byte[] append){
        append(append, 0, append.length);
    }
    public void append(byte[] append, int start, int length){
        // 超出的大小
        int remaining = this.length + length - this.capacity;
        if (0 < remaining) {
            // 计算倍数
            int mult = (int) Math.ceil(remaining / (double)this.expandSize);
            // 扩展数组
            bs = expand(bs, mult * this.expandSize);
            // 更新最大容量
            this.capacity = this.capacity + mult * this.expandSize;
        }
        // 内容更新
        System.arraycopy(append, start, bs, this.length, length);
        // 更新长度
        this.length = this.length + length;
    }
    public byte[] toArray(){
        return Arrays.copyOfRange(bs, 0, length);
    }
    public ByteArray reverse() {
        ByteArray byteArray = new ByteArray(this.length, this.expandSize);
        for (int i = 0; i < this.length; i++) {
            byteArray.append(this.bs[this.length - i - 1]);
        }
        return byteArray;
    }
    public int length() {
        return length;
    }

    public boolean isEmpty() {
        return length() == 0;
    }
    /**
     * 扩展数组大小
     * @param bs 原数组
     * @param length 需要新增的长度
     * @return
     */
    private byte[] expand(byte[] bs, int length){
        int newSize = bs.length + length;
        if (newSize < bs.length) {
            throw new RuntimeException("array overflow");
        }

        if (newSize > maxSize) {
            newSize = maxSize;
        }
        byte[] rst = new byte[newSize];
        System.arraycopy(bs, 0, rst, 0, bs.length);
        return rst;
    }

    public String toString(Charset charset) {
        return new String(bs, 0, length, charset);
    }

    public InputStream toInputStream() {
        return new ByteArrayInputStream(this.bs, 0, length);
    }
    public int transferTo(DataOutput output) throws IOException {
        int length = length();
        output.write(this.bs, 0, length);
        clear();
        return length;
    }
    public String toString() {
        return toString(StandardCharsets.UTF_8);
    }
}
